// This file is licensed under the Elastic License 2.0. Copyright 2021-present, StarRocks Inc.

#pragma once

#include <set>
#include <string>
#include <vector>

#include "fs/fs.h"

namespace starrocks::fs {

StatusOr<std::string> md5sum(const std::string& path);

Status list_dirs_files(const std::string& path, std::set<std::string>* dirs, std::set<std::string>* files);

Status list_dirs_files(FileSystem* fs, const std::string& path, std::set<std::string>* dirs,
                       std::set<std::string>* files);

inline StatusOr<std::unique_ptr<SequentialFile>> new_sequential_file(const std::string& path) {
    ASSIGN_OR_RETURN(auto fs, FileSystem::CreateSharedFromString(path));
    return fs->new_sequential_file(path);
}

inline StatusOr<std::unique_ptr<RandomAccessFile>> new_random_access_file(const std::string& path) {
    ASSIGN_OR_RETURN(auto fs, FileSystem::CreateSharedFromString(path));
    return fs->new_random_access_file(path);
}

inline StatusOr<std::unique_ptr<WritableFile>> new_writable_file(const std::string& path) {
    ASSIGN_OR_RETURN(auto fs, FileSystem::CreateSharedFromString(path));
    return fs->new_writable_file(path);
}

inline StatusOr<std::unique_ptr<WritableFile>> new_writable_file(const WritableFileOptions& opts,
                                                                 const std::string& path) {
    ASSIGN_OR_RETURN(auto fs, FileSystem::CreateSharedFromString(path));
    return fs->new_writable_file(opts, path);
}

inline Status create_directories(const std::string& path) {
    ASSIGN_OR_RETURN(auto fs, FileSystem::CreateSharedFromString(path));
    return fs->create_dir_recursive(path);
}

inline Status delete_file(const std::string& path) {
    ASSIGN_OR_RETURN(auto fs, FileSystem::CreateSharedFromString(path));
    return fs->delete_file(path);
}

inline Status remove(const std::string& path) {
    ASSIGN_OR_RETURN(auto fs, FileSystem::CreateSharedFromString(path));
    ASSIGN_OR_RETURN(auto is_dir, fs->is_directory(path));
    return is_dir ? fs->delete_dir(path) : fs->delete_file(path);
}

inline Status remove(const std::vector<std::string>& paths) {
    for (auto&& path : paths) {
        RETURN_IF_ERROR(remove(path));
    }
    return Status::OK();
}

inline Status remove_all(const std::string& path) {
    ASSIGN_OR_RETURN(auto fs, FileSystem::CreateSharedFromString(path));
    return fs->delete_dir_recursive(path);
}

inline Status get_children(const std::string& path, std::vector<std::string>* files) {
    ASSIGN_OR_RETURN(auto fs, FileSystem::CreateSharedFromString(path));
    return fs->get_children(path, files);
}

inline StatusOr<bool> is_directory(const std::string& path) {
    ASSIGN_OR_RETURN(auto fs, FileSystem::CreateSharedFromString(path));
    return fs->is_directory(path);
}

inline bool path_exist(const std::string& path) {
    auto fs = FileSystem::CreateSharedFromString(path);
    if (!fs.ok()) return false;
    return (*fs)->path_exists(path).ok();
}

// Return the number of bytes copied on success.
inline StatusOr<int64_t> copy(SequentialFile* src, WritableFile* dest, size_t buff_size = 8192) {
    char* buf = new char[buff_size];
    std::unique_ptr<char[]> guard(buf);
    int64_t ncopy = 0;
    while (true) {
        ASSIGN_OR_RETURN(auto nread, src->read(buf, buff_size));
        if (nread == 0) {
            break;
        }
        ncopy += nread;
        RETURN_IF_ERROR(dest->append(Slice(buf, nread)));
    }
    return ncopy;
}

// copy the file from src path to dest path, it will overwrite the existing files
inline Status copy_file(const std::string& src_path, const std::string& dst_path) {
    WritableFileOptions opts{.sync_on_close = true, .mode = FileSystem::CREATE_OR_OPEN_WITH_TRUNCATE};
    ASSIGN_OR_RETURN(auto src_fs, FileSystem::CreateSharedFromString(src_path));
    ASSIGN_OR_RETURN(auto dst_fs, FileSystem::CreateSharedFromString(dst_path));
    ASSIGN_OR_RETURN(auto src_file, src_fs->new_sequential_file(src_path));
    ASSIGN_OR_RETURN(auto dst_file, dst_fs->new_writable_file(opts, dst_path));
    RETURN_IF_ERROR(copy(src_file.get(), dst_file.get()));
    RETURN_IF_ERROR(dst_file->close());
    return Status::OK();
}

inline Status canonicalize(const std::string& path, std::string* real_path) {
    ASSIGN_OR_RETURN(auto fs, FileSystem::CreateSharedFromString(path));
    return fs->canonicalize(path, real_path);
}

} // namespace starrocks::fs
